fix: ApiException null statusCode crash on 2FA#310
Merged
Conversation
ApiException.fromJson() crashed with "type 'Null' is not a subtype of type 'int' in type cast" when the API error response did not include a statusCode field in the JSON body (e.g. TFA_REQUIRED 403 responses). Add httpStatusCode parameter to fromJson() as fallback and pass response.statusCode from all call sites.
KycLevelRequiredException and RegistrationRequiredException now receive and apply the HTTP status code fallback, consistent with the parent ApiException.fromJson() pattern.
…ices - Add regression tests for ApiException.fromJson() covering null statusCode, httpStatusCode fallback, and subclass creation - Migrate all authenticated services to throw ApiException instead of generic Exception: dfx_support_service, dfx_bank_account_service, dfx_blockchain_api_service, dfx_faucet_service, dfx_brokerbot_service (sell endpoints) - Revert unrelated .gitignore change from previous commit
Replace generic Exception with ApiException.fromJson() in: - real_unit_sell_payment_info_service (4 error paths) - real_unit_buy_payment_info_service (catch-all else branch) - real_unit_registration_service (completeRegistration) All authenticated DFX API error responses now consistently use ApiException with httpStatusCode fallback.
9 tasks
TaprootFreak
added a commit
that referenced
this pull request
May 12, 2026
> [!IMPORTANT] > **Merge-blocked on DFXswiss/bitbox_flutter#11.** Without that SDK PR's per-message scoped dedup + 60 s BLE read timeout, the EIP-712 sign aborts at step 1→2 (BLE-layer retransmit corrupts the U2F HID frame stream) and / or after the 10 s timeout fires mid-confirmation. Sequence: merge #11 → tag `v0.0.3` → bump this PR's `pubspec.yaml` from `v0.0.2` to `v0.0.3` → merge here. ## Summary - `KycCubit` hoists the disclaimer + form (name/address) + EIP-712 13-step BitBox sign in front of every state past the email step. Returning users at `level >= requiredLevel` previously dropped straight into `KycCompleted` without ever touching the BitBox; the hardware-wallet ceremony is the security gate, not the backend KYC level. `_bitboxConfirmed` is per-`KycCubit` instance and resets on every KYC entry, so each entry forces a fresh confirmation. - `KycRegistrationSubmitCubit` treats every `ApiException` raised after a successful EIP-712 sign as `RegistrationStatus.completed`. The user has proven hardware-wallet control on the device — the backend's logical reply (already registered, wallet linked to another account, merge required, …) is informational. `KycCubit.checkKyc()` then resolves the next state from the refreshed status, including the existing `KycAccountMergeRequested` page when the wallet is bound to another DFX user. Network / parse / sign errors raise non-`ApiException` types and still surface as a `KycRegistrationSubmitFailure` SnackBar. - `Eip712Signer._signTypedData` now throws when the signature comes back empty or `'0x'`. The bitbox_flutter iOS bridge returns empty bytes when the user cancels mid-sign or the BLE link drops; before this PR the empty signature was POSTed and silently accepted as a successful sign. - TFA handling: the cubit recognises both `statusCode == 403` and `code == 'TFA_REQUIRED'` as the trigger for routing to the 2FA step (paired with #310's `httpStatusCode` fallback so the code path is reachable). - `checkKyc` is wrapped in a 30 s top-level timeout. The email-auto-registration recursion has a one-shot guard so a backend that does not bump the level after `registerEmail` cannot keep the loading spinner alive forever. An `isClosed` guard after the API fetch in `_runCheckKyc` prevents a slow response from overwriting a timeout failure. - `DFXAuthService`: removed the BitBox auth skip from #304 (no longer needed after the v0.0.2 SDK fixes — see inline comment for context). Added a 3 min sign timeout for the BitBox 13-step ceremony, 20 s HTTP timeouts, and an empty-signature guard for personal-message signing. - Email verification shows a localized BitBox sign hint (`registerEmailVerificationBitboxSignHint`) below the confirm button while the signature ceremony is running. ## Test plan - [x] `flutter analyze` — no issues - [x] `flutter test` — 188 tests pass - [ ] Fresh wallet without RealUnit registration: email → disclaimer → form → BitBox 13 fields + sign → ident - [ ] Wallet already attached to the same RealUnit user, KYC level < 30: disclaimer → form → BitBox 13 fields + sign → ident (backend's already-registered reply tolerated, KYC continues) - [ ] **Wallet attached to a different DFX user**: disclaimer → form → BitBox 13 fields + sign → `KycAccountMergePage` ("Identität in anderem Konto gefunden, Merge per Email bestätigen") instead of the generic failure screen - [ ] Returning user already at `level >= 30`: disclaimer + form + BitBox sign first, then `KycCompleted` (no silent grant) - [ ] Cancel the BitBox sign mid-ceremony → `Signature was empty` SnackBar, no silent success - [ ] BitBox not connected on submit → existing modal sheet pops (light theme), reconnect → retry - [ ] Backend response with `code: TFA_REQUIRED` → cubit emits `KycSuccess(twoFa)` instead of `KycFailure` --------- Co-authored-by: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ApiException.fromJson()crashed withtype 'Null' is not a subtype of type 'int' in type castwhen the API error response body didn't contain astatusCodefield (e.g.TFA_REQUIRED403 responses)httpStatusCodefallback parameter tofromJson()and passresponse.statusCodefrom all 18 call sitesTest plan
flutter analyze— no issuesflutter test— 182 tests passed